/**
 * Copyright(c) by JNet Smartware Group.
 * All rights reserved. Not distribution or redistribution without
 * any permission of JNet Smartware Group.
 * 
 * @author thuc
 * 2010/03/15
 */
package vn.smartware.co.java.net.commons;

import java.util.Comparator;
import java.util.List;


/**
 * @author quihtk
 * 
 * Comparator class.This class is used to compare two object. <br>
 * Use Comparable to compare. <br>
 * If is not Comparable (ex: Boolean in jdk1.4), 
 * value will be toString() before compare (compare two String).<br>
 * <br>
 * So, if want to compare LENGTH before compare STRING VALUE, use lengthCompare property. (set TRUE value)
 * 
 */
@SuppressWarnings({"rawtypes","unchecked"})
public class ComparatorImpl implements Comparator {
	/**
	 * default value for sort: accending
	 */
	private static final int defValue = 1;

	/**
	 * 1: ascending, others: descending
	 */
	private int[] sorts;

	/**
	 * a list of property will be sorted.
	 */
	private String[] names;

	/**
	 * true: compare length before compare String value, false (default) not
	 * compare length
	 */
	private boolean lengthCompare = false;

	/**
	 * 
	 * constructor (if don't use search function)
	 * 
	 */
	public ComparatorImpl() {
		super();
	}

	/**
	 * 
	 * constructor (if don't use search function)
	 * 
	 * @param lengthCompare
	 *            true: compare length before compare String value, false
	 *            (default): not compare length
	 * 
	 */
	public ComparatorImpl(boolean lengthCompare) {
		super();
		this.lengthCompare = lengthCompare;
	}

	/**
	 * Constructor with field
	 * 
	 * @param sort
	 *            (1: ascending, others: descending)
	 * @param names
	 *            a list of property will be sorted.
	 * 
	 */
	public ComparatorImpl(String[] names, int sort) {
		super();
		this.sorts = new int[names.length];
		for (int i = 0; i < names.length; i++) {
			this.sorts[i] = sort;
		}
		this.names = names;
	}

	/**
	 * Constructor with field
	 * 
	 * @param sort
	 *            (1: ascending, others: descending)
	 * @param names
	 *            a list of property will be sorted.
	 * @param lengthCompare
	 *            true: compare length before compare String value, false
	 *            (default): not compare length
	 * 
	 */
	public ComparatorImpl(String[] names, int sort, boolean lengthCompare) {
		super();
		this.sorts = new int[names.length];
		for (int i = 0; i < names.length; i++) {
			this.sorts[i] = sort;
		}
		this.names = names;
		this.lengthCompare = lengthCompare;
	}

	/**
	 * 
	 * Constructor with field
	 * 
	 * @param name
	 *            String: name of property will be sorted
	 * @param sort
	 *            (1: ascending, others: descending)
	 */
	public ComparatorImpl(String name, int sort) {
		super();
		this.sorts = new int[] { sort };
		this.names = new String[] { name };
	}

	/**
	 * 
	 * Constructor with field
	 * 
	 * @param name
	 *            String: name of property will be sorted
	 * @param sort
	 *            (1: ascending, others: descending)
	 * @param lengthCompare
	 *            true: compare length before compare String value, false
	 *            (default): not compare length
	 */
	public ComparatorImpl(String name, int sort, boolean lengthCompare) {
		super();
		this.sorts = new int[] { sort };
		this.names = new String[] { name };
		this.lengthCompare = lengthCompare;
	}

	/**
	 * 
	 * Constructor with field
	 * 
	 * @param name
	 *            String: name of property will be sorted
	 */
	public ComparatorImpl(String name) {
		super();
		this.sorts = new int[] { defValue };
		this.names = new String[] { name };
	}

	/**
	 * 
	 * Constructor with field
	 * 
	 * @param name
	 *            String: name of property will be sorted
	 * @param lengthCompare
	 *            true: compare length before compare String value, false
	 *            (default): not compare length
	 */
	public ComparatorImpl(String name, boolean lengthCompare) {
		super();
		this.sorts = new int[] { defValue };
		this.names = new String[] { name };
		this.lengthCompare = lengthCompare;
	}

	/**
	 * Constructor with list of SortInfo<br>
	 * purpose: sort key1 as ascending, key2 as descending....<br>
	 * SortInfo has key (key sort) and sort<br>
	 * for detail of SortInfo, See jp.co.nikkocompany.air.util.SortInfo
	 * 
	 * @param sortList
	 */
	public ComparatorImpl(List sortList) {
		super();
		if (sortList == null || sortList.isEmpty()) {
			return;
		}
		this.sorts = new int[sortList.size()];
		this.names = new String[sortList.size()];
		for (int i = 0; i < sortList.size(); i++) {
			SortInfo sortInfo = (SortInfo) sortList.get(i);
			if (sortInfo == null) {
				continue;
			}
			this.sorts[i] = sortInfo.getSort();
			this.names[i] = sortInfo.getKey();
		}
	}

	/**
	 * Constructor with list of SortInfo<br>
	 * purpose: sort key1 as ascending, key2 as descending....<br>
	 * SortInfo has key (key sort) and sort<br>
	 * for detail of SortInfo, See jp.co.nikkocompany.air.util.SortInfo
	 * 
	 * @param sortList
	 * @param lengthCompare
	 *            true: compare length before compare String value, false
	 *            (default): not compare length
	 */
	public ComparatorImpl(List sortList, boolean lengthCompare) {
		super();
		if (sortList == null || sortList.isEmpty()) {
			return;
		}
		this.sorts = new int[sortList.size()];
		this.names = new String[sortList.size()];
		for (int i = 0; i < sortList.size(); i++) {
			SortInfo sortInfo = (SortInfo) sortList.get(i);
			if (sortInfo == null) {
				continue;
			}
			this.sorts[i] = sortInfo.getSort();
			this.names[i] = sortInfo.getKey();
		}
		this.lengthCompare = lengthCompare;
	}

	/**
	 * Compares its two arguments for order. Returns a negative integer, zero,
	 * or a positive integer as the first argument is less than, equal to, or
	 * greater than the second.
	 * <p>
	 * 
	 * @param o1
	 * @param o2
	 * @return int
	 */
	public int compare(Object o1, Object o2) {
		if (names == null || names.length <= 0) {
			return 0;
		}
		int i = 0;
		while (i < this.names.length) {
			Object value1 = getValue(o1, i);
			Object value2 = getValue(o2, i);
			int compare = comp(value1, value2);
			int sort = this.sorts[i];
			if (sort == 1) {// ascending
				if (compare < 0) {
					return -1;
				} else if (compare > 0) {
					return 1;
				}
			} else {// descing
				if (compare < 0) {
					return 1;
				} else if (compare > 0) {
					return -1;
				}
			}
			// when same, go to next name (prop)
			i++;
		}
		return 0;
	}
	
	
	private int comp(Object obj1, Object obj2) {				
		if (Utils.isNullEmpty(obj1)) {
			if (Utils.isNullEmpty(obj2)) {// the same (obj1=null, obj2=null)
				return 0;
			}
			return -1;// obj1 = null, obj2 != null
		}
		if (Utils.isNullEmpty(obj2)) {// obj1 != null, obj2=null
			return 1;
		}
		
		if (lengthCompare) {//when lengthCompare: compare length first
			if (obj1.toString().length() > obj2.toString().length()) {
				return 1;
			}
			if (obj1.toString().length() < obj2.toString().length()) {
				return -1;
			}
		}
		
		try {
			return ((Comparable)obj1).compareTo(obj2);
		} catch (Exception e) {
			//Jdk1.4 some Type class not comparable (ClassCastException)
			//other exception -> compare with String type
			return obj1.toString().compareTo(obj2.toString());
		}
	}
	
	
	/**
	 * get value of names[index] prop
	 * 
	 * @return Object
	 * @param o
	 * @param index 
	 */
	private Object getValue(Object o, int index) {
		if (o == null) {
			return null;
		}
		String name = this.names[index];
		return BeanUtil.getPropertyValue(o, name);		
	}
}